home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / shllutil.lha / shellutils-1.8 / src / printf.c < prev    next >
C/C++ Source or Header  |  1992-06-04  |  10KB  |  482 lines

  1. /* printf - format and print data
  2.    Copyright (C) 1990, 1991 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Usage: printf format [argument...]
  19.  
  20.    A front end to the printf function that lets it be used from the shell.
  21.  
  22.    Backslash escapes:
  23.  
  24.    \" = double quote
  25.    \\ = backslash
  26.    \a = alert (bell)
  27.    \b = backspace
  28.    \c = produce no further output
  29.    \f = form feed
  30.    \n = new line
  31.    \r = carriage return
  32.    \t = horizontal tab
  33.    \v = vertical tab
  34.    \0ooo = octal number (ooo is 0 to 3 digits)
  35.    \xhhh = hexadecimal number (hhh is 1 to 3 digits)
  36.  
  37.    Additional directive:
  38.  
  39.    %b = print an argument string, interpreting backslash escapes
  40.  
  41.    The `format' argument is re-used as many times as necessary
  42.    to convert all of the given arguments.
  43.  
  44.    David MacKenzie <djm@gnu.ai.mit.edu> */
  45.  
  46. #include <stdio.h>
  47. #include <ctype.h>
  48. #include <sys/types.h>
  49. #include "system.h"
  50.  
  51. #ifndef STDC_HEADERS
  52. double strtod ();
  53. long strtol ();
  54. unsigned long strtoul ();
  55. #endif
  56.  
  57. #define isodigit(c) ((c) >= '0' && (c) <= '7')
  58. #define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
  59. #define octtobin(c) ((c) - '0')
  60.  
  61. char *xmalloc ();
  62. double xstrtod ();
  63. int print_esc ();
  64. int print_formatted ();
  65. long xstrtol ();
  66. unsigned long xstrtoul ();
  67. void error ();
  68. void print_direc ();
  69. void print_esc_char ();
  70. void print_esc_string ();
  71. void verify ();
  72.  
  73. /* The name this program was run with. */
  74. char *program_name;
  75.  
  76. /* The value to return to the calling program.  */
  77. int exit_status;
  78.  
  79. void
  80. main (argc, argv)
  81.      int argc;
  82.      char **argv;
  83. {
  84.   char *format;
  85.   int args_used;
  86.  
  87.   program_name = argv[0];
  88.   exit_status = 0;
  89.  
  90.   if (argc == 1)
  91.     {
  92.       fprintf (stderr, "Usage: %s format [argument...]\n", program_name);
  93.       exit (1);
  94.     }
  95.  
  96.   format = argv[1];
  97.   argc -= 2;
  98.   argv += 2;
  99.  
  100.   do
  101.     {
  102.       args_used = print_formatted (format, argc, argv);
  103.       argc -= args_used;
  104.       argv += args_used;
  105.     }
  106.   while (args_used > 0 && argc > 0);
  107.  
  108.   exit (exit_status);
  109. }
  110.  
  111. /* Print the text in FORMAT, using ARGV (with ARGC elements) for
  112.    arguments to any `%' directives.
  113.    Return the number of elements of ARGV used.  */
  114.  
  115. int
  116. print_formatted (format, argc, argv)
  117.      char *format;
  118.      int argc;
  119.      char **argv;
  120. {
  121.   int save_argc = argc;        /* Preserve original value.  */
  122.   char *f;            /* Pointer into `format'.  */
  123.   char *direc_start;        /* Start of % directive.  */
  124.   int direc_length;        /* Length of % directive.  */
  125.   int field_width;        /* Arg to first '*', or -1 if none.  */
  126.   int precision;        /* Arg to second '*', or -1 if none.  */
  127.  
  128.   for (f = format; *f; ++f)
  129.     {
  130.       switch (*f)
  131.     {
  132.     case '%':
  133.       direc_start = f++;
  134.       direc_length = 1;
  135.       field_width = precision = -1;
  136.       if (*f == '%')
  137.         {
  138.           putchar ('%');
  139.           break;
  140.         }
  141.       if (*f == 'b')
  142.         {
  143.           if (argc > 0)
  144.         {
  145.           print_esc_string (*argv);
  146.           ++argv;
  147.           --argc;
  148.         }
  149.           break;
  150.         }
  151.       if (index ("-+ #", *f))
  152.         {
  153.           ++f;
  154.           ++direc_length;
  155.         }
  156.       if (*f == '*')
  157.         {
  158.           ++f;
  159.           ++direc_length;
  160.           if (argc > 0)
  161.         {
  162.           field_width = xstrtoul (*argv);
  163.           ++argv;
  164.           --argc;
  165.         }
  166.           else
  167.         field_width = 0;
  168.         }
  169.       else
  170.         while (isdigit (*f))
  171.           {
  172.         ++f;
  173.         ++direc_length;
  174.           }
  175.       if (*f == '.')
  176.         {
  177.           ++f;
  178.           ++direc_length;
  179.           if (*f == '*')
  180.         {
  181.           ++f;
  182.           ++direc_length;
  183.           if (argc > 0)
  184.             {
  185.               precision = xstrtoul (*argv);
  186.               ++argv;
  187.               --argc;
  188.             }
  189.           else
  190.             precision = 0;
  191.         }
  192.           else
  193.         while (isdigit (*f))
  194.           {
  195.             ++f;
  196.             ++direc_length;
  197.           }
  198.         }
  199.       if (*f == 'l' || *f == 'L' || *f == 'h')
  200.         {
  201.           ++f;
  202.           ++direc_length;
  203.         }
  204.       if (!index ("diouxXfeEgGcs", *f))
  205.         error (1, 0, "%%%c: invalid directive", *f);
  206.       ++direc_length;
  207.       if (argc > 0)
  208.         {
  209.           print_direc (direc_start, direc_length, field_width,
  210.                precision, *argv);
  211.           ++argv;
  212.           --argc;
  213.         }
  214.       else
  215.         print_direc (direc_start, direc_length, field_width,
  216.              precision, "");
  217.       break;
  218.  
  219.     case '\\':
  220.       f += print_esc (f);
  221.       break;
  222.  
  223.     default:
  224.       putchar (*f);
  225.     }
  226.     }
  227.  
  228.   return save_argc - argc;
  229. }
  230.  
  231. /* Print a \ escape sequence starting at ESCSTART.
  232.    Return the number of characters in the escape sequence
  233.    besides the backslash. */
  234.  
  235. int
  236. print_esc (escstart)
  237.      char *escstart;
  238. {
  239.   register char *p = escstart + 1;
  240.   int esc_value = 0;        /* Value of \nnn escape. */
  241.   int esc_length;        /* Length of \nnn escape. */
  242.  
  243.   /* \0ooo and \xhhh escapes have maximum length of 3 chars. */
  244.   if (*p == 'x')
  245.     {
  246.       for (esc_length = 0, ++p;
  247.        esc_length < 3 && isxdigit (*p);
  248.        ++esc_length, ++p)
  249.     esc_value = esc_value * 16 + hextobin (*p);
  250.       if (esc_length == 0)
  251.     error (1, 0, "missing hexadecimal number in escape");
  252.       putchar (esc_value);
  253.     }
  254.   else if (*p == '0')
  255.     {
  256.       for (esc_length = 0, ++p;
  257.        esc_length < 3 && isodigit (*p);
  258.        ++esc_length, ++p)
  259.     esc_value = esc_value * 8 + octtobin (*p);
  260.       putchar (esc_value);
  261.     }
  262.   else if (index ("\"\\abcfnrtv", *p))
  263.     print_esc_char (*p++);
  264.   else
  265.     error (1, 0, "\\%c: invalid escape", *p);
  266.   return p - escstart - 1;
  267. }
  268.  
  269. /* Output a single-character \ escape.  */
  270.  
  271. void
  272. print_esc_char (c)
  273.      char c;
  274. {
  275.   switch (c)
  276.     {
  277.     case 'a':            /* Alert. */
  278.       putchar (7);
  279.       break;
  280.     case 'b':            /* Backspace. */
  281.       putchar (8);
  282.       break;
  283.     case 'c':            /* Cancel the rest of the output. */
  284.       exit (0);
  285.       break;
  286.     case 'f':            /* Form feed. */
  287.       putchar (12);
  288.       break;
  289.     case 'n':            /* New line. */
  290.       putchar (10);
  291.       break;
  292.     case 'r':            /* Carriage return. */
  293.       putchar (13);
  294.       break;
  295.     case 't':            /* Horizontal tab. */
  296.       putchar (9);
  297.       break;
  298.     case 'v':            /* Vertical tab. */
  299.       putchar (11);
  300.       break;
  301.     default:
  302.       putchar (c);
  303.       break;
  304.     }
  305. }
  306.  
  307. /* Print string STR, evaluating \ escapes. */
  308.  
  309. void
  310. print_esc_string (str)
  311.      char *str;
  312. {
  313.   for (; *str; str++)
  314.     if (*str == '\\')
  315.       str += print_esc (str);
  316.     else
  317.       putchar (*str);
  318. }
  319.  
  320. /* Output a % directive.  START is the start of the directive,
  321.    LENGTH is its length, and ARGUMENT is its argument.
  322.    If FIELD_WIDTH or PRECISION is non-negative, they are args for
  323.    '*' values in those fields. */
  324.  
  325. void
  326. print_direc (start, length, field_width, precision, argument)
  327.      char *start;
  328.      int length;
  329.      int field_width;
  330.      int precision;
  331.      char *argument;
  332. {
  333.   char *p;        /* Null-terminated copy of % directive. */
  334.  
  335.   p = xmalloc ((unsigned) (length + 1));
  336.   strncpy (p, start, length);
  337.   p[length] = 0;
  338.  
  339.   switch (p[length - 1])
  340.     {
  341.     case 'd':
  342.     case 'i':
  343.       if (field_width < 0)
  344.     {
  345.       if (precision < 0)
  346.         printf (p, xstrtol (argument));
  347.       else
  348.         printf (p, precision, xstrtol (argument));
  349.     }
  350.       else
  351.     {
  352.       if (precision < 0)
  353.         printf (p, field_width, xstrtol (argument));
  354.       else
  355.         printf (p, field_width, precision, xstrtol (argument));
  356.     }
  357.       break;
  358.  
  359.     case 'o':
  360.     case 'u':
  361.     case 'x':
  362.     case 'X':
  363.       if (field_width < 0)
  364.     {
  365.       if (precision < 0)
  366.         printf (p, xstrtoul (argument));
  367.       else
  368.         printf (p, precision, xstrtoul (argument));
  369.     }
  370.       else
  371.     {
  372.       if (precision < 0)
  373.         printf (p, field_width, xstrtoul (argument));
  374.       else
  375.         printf (p, field_width, precision, xstrtoul (argument));
  376.     }
  377.       break;
  378.  
  379.     case 'f':
  380.     case 'e':
  381.     case 'E':
  382.     case 'g':
  383.     case 'G':
  384.       if (field_width < 0)
  385.     {
  386.       if (precision < 0)
  387.         printf (p, xstrtod (argument));
  388.       else
  389.         printf (p, precision, xstrtod (argument));
  390.     }
  391.       else
  392.     {
  393.       if (precision < 0)
  394.         printf (p, field_width, xstrtod (argument));
  395.       else
  396.         printf (p, field_width, precision, xstrtod (argument));
  397.     }
  398.       break;
  399.  
  400.     case 'c':
  401.       printf (p, *argument);
  402.       break;
  403.  
  404.     case 's':
  405.       if (field_width < 0)
  406.     {
  407.       if (precision < 0)
  408.         printf (p, argument);
  409.       else
  410.         printf (p, precision, argument);
  411.     }
  412.       else
  413.     {
  414.       if (precision < 0)
  415.         printf (p, field_width, argument);
  416.       else
  417.         printf (p, field_width, precision, argument);
  418.     }
  419.       break;
  420.     }
  421.  
  422.   free (p);
  423. }
  424.  
  425. unsigned long
  426. xstrtoul (s)
  427.      char *s;
  428. {
  429.   char *end;
  430.   unsigned long val;
  431.  
  432.   errno = 0;
  433.   val = strtoul (s, &end, 0);
  434.   verify (s, end);
  435.   return val;
  436. }
  437.  
  438. long
  439. xstrtol (s)
  440.      char *s;
  441. {
  442.   char *end;
  443.   long val;
  444.  
  445.   errno = 0;
  446.   val = strtol (s, &end, 0);
  447.   verify (s, end);
  448.   return val;
  449. }
  450.  
  451. double
  452. xstrtod (s)
  453.      char *s;
  454. {
  455.   char *end;
  456.   double val;
  457.  
  458.   errno = 0;
  459.   val = strtod (s, &end);
  460.   verify (s, end);
  461.   return val;
  462. }
  463.  
  464. void
  465. verify (s, end)
  466.      char *s, *end;
  467. {
  468.   if (errno)
  469.     {
  470.       error (0, errno, "%s", s);
  471.       exit_status = 1;
  472.     }
  473.   else if (*end)
  474.     {
  475.       if (s == end)
  476.     error (0, 0, "%s: expected a numeric value", s);
  477.       else
  478.     error (0, 0, "%s: value not completely converted", s);
  479.       exit_status = 1;
  480.     }
  481. }
  482.